// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaArgs;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.util.Log;
public class BackgroundPlugin extends CordovaPlugin {
private static final String LOG_TAG = "BackgroundPlugin";
// This reference lets us know whether or not the app is currently running.
static BackgroundPlugin pluginInstance;
private CallbackContext messageChannel;
private boolean isMainInstance;
private boolean resumeEventSeen;
@Override
public void pluginInitialize() {
isMainInstance = pluginInstance == null;
if (isMainInstance) {
pluginInstance = this;
}
}
@Override
public void onReset() {
messageChannel = null;
if (isMainInstance) {
releasePluginMessageChannels();
}
}
@Override
public void onResume(boolean multitasking) {
// Ignore the resume on start-up, but assume messageChannel means there was no onResume on start-up.
// Cordova has flip-flopped on whether to send this event on start-up :S.
if (!resumeEventSeen && messageChannel == null) {
resumeEventSeen = true;
return;
}
if (isMainInstance && BackgroundActivity.topInstance != null) {
final BackgroundActivity topInstance = BackgroundActivity.topInstance;
BackgroundActivity.topInstance = null;
// Kill off the BackgroundActivity task stack. Leaving it around causes the next call
// to BackgroundActivity.launchBackground() to have MainActivity in the wrong task stack.
webView.getView().postDelayed(new Runnable() {
@Override
public void run() {
Log.i(LOG_TAG, "Finishing background activity now that foreground is alive");
topInstance.finish();
}
}, 50);
}
String switchType = BackgroundActivity.prevLaunchWasProgrammatic ? "programmatic" : "normal";
sendEventMessage("foreground", switchType);
if (isMainInstance) {
webView.getView().post(new Runnable() {
@Override
public void run() {
BackgroundActivity.prevLaunchWasProgrammatic = false;
}
});
}
}
@Override
public void onDestroy() {
messageChannel = null;
if (isMainInstance) {
pluginInstance = null;
releasePluginMessageChannels();
}
}
@Override
public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
if ("messageChannel".equals(action)) {
messageChannel = callbackContext;
sendEventMessage("startup", BackgroundActivity.topInstance != null);
return true;
} else if ("show".equals(action)) {
cordova.getThreadPool().execute(new Runnable() {
@Override
public void run() {
show(callbackContext);
}
});
return true;
}
return false;
}
private void sendEventMessage(String action, Object value) {
if (messageChannel == null) {
Log.w(LOG_TAG, "Message being dropped since channel not yet established: " + action);
return;
}
JSONObject obj = new JSONObject();
try {
obj.put("type", action);
obj.put("value", value);
} catch (JSONException e) {
Log.e(LOG_TAG, "Failed to create background event", e);
}
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, obj);
pluginResult.setKeepCallback(true);
messageChannel.sendPluginResult(pluginResult);
}
private void show(final CallbackContext callbackContext) {
Activity activity = cordova.getActivity();
if (!activity.hasWindowFocus()) {
BackgroundActivity.launchForeground(activity, false);
}
callbackContext.success();
}
private static void releasePluginMessageChannels() {
// Release the message channel for all plugins using our background event handler
// - The Cordova Plugin framework does not provide a direct way to handle the life cycle
// events for plugins (e.g. onReset, onDestroy)
// - To avoid extra boilerplate in any plugin using the event handler, will cleanup all
// the message channels for plugins here
BackgroundEventHandler.releaseMessageChannels();
}
}